<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="test">test</div>

    <ul id="fragment_test">
      <li>test1</li>
      <li>test2</li>
      <li>test3</li>
    </ul>

    <!--
1. [].slice.call(lis): 将伪数组转换为真数组
2. node.nodeType: 得到节点类型
3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
6. DocumentFragment: 文档碎片(高效批量更新多个节点)
-->

    <script type="text/javascript">
      //1. [].slice.call(lis): 根据伪数组生成对应的真数组
      const lis = document.getElementsByTagName("li") // lis是伪数组(是一个特别的对象, length和数值下标属性)
      console.log(lis instanceof Object, lis instanceof Array)
      // 数组的slice()截取数组中指定部分的元素, 生成一个新的数组  [1, 3, 5, 7, 9], slice(0, 3)
      // slice2()
      Array.prototype.slice2 = function(start, end) {
        start = start || 0
        end = start || this.length
        const arr = []
        for (var i = start; i < end; i++) {
          arr.push(this[i])
        }
        return arr
      }
      const lis2 = Array.prototype.slice.call(lis) // lis.slice()
      console.log(lis2 instanceof Object, lis2 instanceof Array)
      // lis2.forEach()

      //2. node.nodeType: 得到节点类型
      const elementNode = document.getElementById("test")
      const attrNode = elementNode.getAttributeNode("id")
      const textNode = elementNode.firstChild
      console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType)

      //3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
      const obj = {
        firstName: "A",
        lastName: "B"
      }
      //obj.fullName = 'A-B'
      Object.defineProperty(obj, "fullName", {
        // 属性描述符:

        // 数据描述符

        //访问描述符
        // 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
        get() {
          return this.firstName + "-" + this.lastName
        },
        // 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
        set(value) {
          const names = value.split("-")
          this.firstName = names[0]
          this.lastName = names[1]
        }
      })

      console.log(obj.fullName) // A-B
      obj.fullName = "C-D"
      console.log(obj.firstName, obj.lastName) // C D

      Object.defineProperty(obj, "fullName2", {
        configurable: false, //是否可以重新define
        enumerable: true, // 是否可以枚举(for..in / keys())
        value: "A-B", // 指定初始值
        writable: false // value是否可以修改
      })
      console.log(obj.fullName2) // A-B
      obj.fullName2 = "E-F"
      console.log(obj.fullName2) // A-B

      /*Object.defineProperty(obj, 'fullName2', {
    configurable: true,
    enumerable: true,
    value: 'G-H',
    writable: true
  })*/

      //4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
      const names = Object.keys(obj)
      console.log(names)

      //5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
      console.log(
        obj.hasOwnProperty("fullName"),
        obj.hasOwnProperty("toString")
      ) // true false

      //6. DocumentFragment: 文档碎片(高效批量更新多个节点)
      // document: 对应显示的页面, 包含n个elment  一旦更新document内部的某个元素界面更新
      // documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新framgnet中的某个element, 界面不变
      /*
  <ul id="fragment_test">
    <li>test1</li>
    <li>test2</li>
    <li>test3</li>
  </ul>
   */
      const ul = document.getElementById("fragment_test")
      // 1. 创建fragment
      const fragment = document.createDocumentFragment()
      // 2. 取出ul中所有子节点取出保存到fragment
      let child
      while ((child = ul.firstChild)) {
        // 一个节点只能有一个父亲
        fragment.appendChild(child) // 先将child从ul中移除, 添加为fragment子节点
      }

      // 3. 更新fragment中所有li的文本
      Array.prototype.slice.call(fragment.childNodes).forEach(node => {
        if (node.nodeType === 1) {
          // 元素节点 <li>
          node.textContent = "vue"
        }
      })

      // 4. 将fragment插入ul
      ul.appendChild(fragment)
    </script>
  </body>
</html>
